package service

import (
	"context"
	"encoding/json"
	"fmt"
	mathRand "math/rand/v2"
	"smart-flow/server/config"
	"smart-flow/server/executor"
	"smart-flow/server/goal"
	"smart-flow/server/ips"
	"smart-flow/server/logger"
	"smart-flow/server/monitor"
	"smart-flow/server/net"
	"smart-flow/server/redis"
	"smart-flow/server/utils"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/google/uuid"
	"go.uber.org/zap"
)

func invokeEnhanceForStart(isp string, target *goal.Target, duration, rate int, scanChanSlice []chan *ScanChanData) {
	wg := new(sync.WaitGroup)
	for _, scanChan := range scanChanSlice {
		wg.Add(1)
		go func(scanChan chan *ScanChanData) {
			defer wg.Done()
			for client := range scanChan {
				enhanceForStart(client, isp, target, duration, rate)
			}
		}(scanChan)
	}
	wg.Wait()
}

func invokeBalanceEnhanceForStart(isp string, targets []*goal.Target, durationRefer, rate int, scanChanSlice []chan *ScanChanData) {
	wg := new(sync.WaitGroup)
	var (
		success int32
		fail    int32
	)
	hour := time.Now().Hour()
	for _, scanChan := range scanChanSlice {
		wg.Add(1)
		go func(scanChan chan *ScanChanData) {
			defer wg.Done()
			deflection := config.AppConfig().Dy.Deflection
			rotation := false
			for client := range scanChan {
				var target *goal.Target
				if client.NeedV6 {
					if !rotation {
						target = findNoLimitTargetV6(targets)
					} else {
						target = findNoLimitTargetV4(targets)
					}
					if deflection == 1 {
						rotation = !rotation
					}
				} else {
					target = findNoLimitTargetV4(targets)
				}
				if target == nil {
					logger.Logger.Error("[业务] 启动模块Agent目标异常", zap.String("uid:", client.Uid), zap.String("isp:", config.GetIspHuman(isp)))
					return
				}
				duration := durationRefer
				if durationRefer == 0 { // 任务单次Agent运行时间最长2~4小时，设备可能会关闭，太长无意义
					duration = config.TaskAgentDuration + mathRand.IntN(config.TaskAgentDuration)
				}
				if hour >= 23 || hour < 6 { // 谷时速率增量
					rate += config.AppConfig().Dy.OffPeakIncr
				}
				if enhanceForStart(client, isp, target, duration, rate) {
					atomic.AddInt32(&success, 1)
					time.Sleep(5 * time.Second)
				} else {
					atomic.AddInt32(&fail, 1)
				}
			}
		}(scanChan)
	}
	wg.Wait()
	logger.Logger.Info("[业务] 执行结果", zap.Int32("success", success), zap.Int32("fail", fail))
}

func invokeEnhanceForStop(isp string, scanChanSlice []chan *ScanChanData) {
	wg := new(sync.WaitGroup)
	for _, scanChan := range scanChanSlice {
		wg.Add(1)
		go func(scanChan chan *ScanChanData) {
			defer wg.Done()
			i := 0
			for client := range scanChan {
				enhanceForStop(client, isp)
				i++
			}
		}(scanChan)
	}
	wg.Wait()
	// redis.RDB.Del(context.Background(), utils.GenIspAnyKey(config.FlowManageKey, isp))
}

func enhanceForStart(client *ScanChanData, isp string, target *goal.Target, duration, rate int) bool {
	uid := client.Uid
	ip := client.Ip
	sessionId := uuid.New().String()
	index := net.GenConsoleIndex()
	logger.Logger.Info("[业务] 启动模块Agent增强开始", zap.String("sessionId", sessionId), zap.String("uid:", uid),
		zap.String("ip:", ip),
		zap.String("isp:", config.GetIspHuman(isp)),
		zap.Any("target", target.TargetCtx),
		zap.Int("duration:", duration),
		zap.Int("rate:", rate),
		zap.Int("index", index))
	agent := new(net.Agent)
	err := agent.InitAgent(sessionId, uid, ip, isp, &target.TargetCtx, duration, rate, index, client.NeedV6)
	if err != nil {
		logger.Logger.Error("[业务] 启动模块Agent初始化异常", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)), zap.String("ip:", ip),
			zap.String("isp:", config.GetIspHuman(isp)), zap.Error(err))
		return false
	}
	if !NotifyUserConnect(sessionId, uid, index) {
		logger.Logger.Error("[业务] 启动模块创建Agent会话异常", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)), zap.String("ip:", ip),
			zap.String("isp:", config.GetIspHuman(isp)))
		return false
	}
	net.ConsoleInstance(index).AddSession(sessionId, agent)
	defer NotifyUserDisconnect(sessionId, uid, index)
	timer := time.NewTimer(15 * time.Second)
	defer timer.Stop()
	timerAll := time.NewTimer(120 * time.Second)
	defer timerAll.Stop()
	for {
		select {
		case status := <-agent.ManageChan:
			switch status {
			case config.AgentReady:
				// logger.Logger.Info("[业务] 启动模块Agent增强安全通道建立成功", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				go doEnhanceForStart(agent, sessionId, uid, isp, target, duration, rate)
			case config.AgentExecDoing:
				// logger.Logger.Info("[业务] 启动模块Agent增强执行中", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
			case config.AgentConnected:
				// logger.Logger.Info("[业务] 启动模块Agent增强安全通道开始建立", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
			case config.AgentSuccess:
				// logger.Logger.Info("[业务] 启动模块Agent增强完成", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				saveResult(uid, true, agent)
				return true
			case config.AgentRunning:
				logger.Logger.Warn("[业务] 启动模块Agent增强正在运行中", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				return false
			case config.AgentNotSupportV6:
				logger.Logger.Warn("[业务] 启动模块Agent增强不支持IPv6", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				saveNotSupportV6(uid, agent)
				return false
			case config.AgentExcept:
				logger.Logger.Error("[业务] 启动模块Agent增强异常", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				return false
			case config.AgentDisconnected:
				logger.Logger.Info("[业务] 启动模块Agent增强断开", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				return false
			case config.AgentExecTimeout:
				// logger.Logger.Error("[业务] 启动模块Agent增强超时", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				executor.FailureInstance(isp).AddLoser(uid, time.Now().Unix())
				return false
			default:
				logger.Logger.Error("[业务] 启动模块Agent未知状态", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)), zap.Int("status", status))
			}
		case <-timer.C:
			switch agent.Context.Status {
			case config.AgentInit:
				logger.Logger.Error("[业务] 启动模块Agent已丢失", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				executor.FailureInstance(isp).AddLoser(uid, time.Now().Unix())
				return false
			case config.AgentConnected:
				logger.Logger.Error("[业务] 启动模块Agent太卡了", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				executor.FailureInstance(isp).AddLoser(uid, time.Now().Unix())
				return false
			default:
				continue
			}
		case <-timerAll.C:
			logger.Logger.Error("[业务] 启动模块执行太久了...", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
			return false
		}
	}
}

func enhanceForStop(client *ScanChanData, isp string) bool {
	uid := client.Uid
	ip := client.Ip
	logger.Logger.Info("[业务] 停止模块Agent开始增强", zap.String("uid:", uid), zap.String("isp:", config.GetIspHuman(isp)))
	sessionId := uuid.New().String()
	index := net.GenConsoleIndex()
	agent := new(net.Agent)
	err := agent.InitAgent(sessionId, uid, ip, isp, &goal.TargetCtx{}, 0, 0, index, client.NeedV6)
	if err != nil {
		logger.Logger.Error("[业务] 停止模块Agent初始化异常", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp:", config.GetIspHuman(isp)), zap.Error(err))
		return false
	}
	if !NotifyUserConnect(sessionId, uid, index) {
		logger.Logger.Error("[业务] 停止模块创建Agent会话异常", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp:", config.GetIspHuman(isp)))
		return false
	}
	net.ConsoleInstance(index).AddSession(sessionId, agent)
	defer NotifyUserDisconnect(sessionId, uid, index)
	timer := time.NewTimer(15 * time.Second)
	defer timer.Stop()
	timerAll := time.NewTimer(120 * time.Second)
	defer timerAll.Stop()
	for {
		select {
		case status := <-agent.ManageChan:
			switch status {
			case config.AgentReady:
				// logger.Logger.Info("[业务] 停止模块Agent增强安全通道建立成功", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				go doEnhanceForStop(agent, sessionId, uid, isp)
			case config.AgentExecDoing:
				// logger.Logger.Info("[业务] 停止模块Agent增强执行中", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
			case config.AgentConnected:
				// logger.Logger.Info("[业务] 停止模块Agent增强安全通道开始建立", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
			case config.AgentSuccess:
				// logger.Logger.Info("[业务] 停止模块Agent增强完成", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				saveResult(uid, false, agent)
				return true
			case config.AgentExcept:
				logger.Logger.Error("[业务] 停止模块Agent增强异常", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				return false
			case config.AgentDisconnected:
				logger.Logger.Info("[业务] 停止模块Agent增强断开", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				return false
			case config.AgentExecTimeout:
				// logger.Logger.Error("[业务] 停止模块Agent增强超时", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				return false
			default:
				logger.Logger.Error("[业务] 停止模块Agent未知状态", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)), zap.Int("status", status))
			}
		case <-timer.C:
			switch agent.Context.Status {
			case config.AgentInit:
				logger.Logger.Error("[业务] 停止模块Agent已丢失", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				return false
			case config.AgentConnected:
				logger.Logger.Error("[业务] 停止模块Agent太卡了", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
				executor.FailureInstance(isp).AddLoser(uid, time.Now().Unix())
				return false
			default:
				continue
			}
		case <-timerAll.C:
			logger.Logger.Error("[业务] 停止模块执行太久了...", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("isp", config.GetIspHuman(isp)))
			return false
		}
	}
}

func doEnhanceForStart(agent *net.Agent, sessionId, uid, isp string, target *goal.Target, duration, rate int) {
	if isp == config.IspMobile {
		ip := ips.GetCmIp() // 获取移动提供的IP
		if ip == "" {
			logger.Logger.Error("[业务] 获取移动IP失败...", zap.String("sessionId", sessionId), zap.String("uid:", uid))
			agent.ManageChan <- config.AgentExcept
			return
		}
		doEnhanceForStart4P(agent, sessionId, uid, ip, target, duration, rate)
	} else {
		doEnhanceForStart4A(agent, sessionId, uid, target, duration, rate)
	}
}

func doEnhanceForStart4A(agent *net.Agent, sessionId, uid string, target *goal.Target, duration, rate int) {
	cmd := fmt.Sprintf("cd /tmp && echo start:$(echo $((88+11)) && ls sfa_%s_$(busybox uname -m) 2>/dev/null); \r\n\n", config.AppConfig().Agent.Ver)
	res := doExecCmd(agent, sessionId, uid, cmd, "start:99", false, 10*time.Second)
	if strings.Contains(res, "sfa_"+config.AppConfig().Agent.Ver) {
		doEnhanceWithoutUpgrade4A(agent, sessionId, uid, config.AppConfig().Agent.Ver, target, duration, rate)
	} else {
		doEnhanceWithUpgrade4A(agent, sessionId, uid, config.AppConfig().Agent.Ver, target, duration, rate)
	}
}

func doEnhanceForStart4P(agent *net.Agent, sessionId, uid, ip string, target *goal.Target, duration, rate int) {
	cmd := fmt.Sprintf("cd /tmp && echo start:$(echo $((88+11)) && ls sfp_%s_$(busybox uname -m) 2>/dev/null); \r\n\n", config.AppConfig().Pusher.Ver)
	res := doExecCmd(agent, sessionId, uid, cmd, "start:99", false, 10*time.Second)
	if strings.Contains(res, "sfp_"+config.AppConfig().Agent.Ver) {
		doEnhanceWithoutUpgrade4P(agent, sessionId, uid, ip, config.AppConfig().Pusher.Ver, target, duration, rate)
	} else {
		doEnhanceWithUpgrade4P(agent, sessionId, uid, ip, config.AppConfig().Pusher.Ver, target, duration, rate)
	}
}

func doEnhanceForStop(agent *net.Agent, sessionId, uid, isp string) {
	if isp == config.IspMobile {
		doEnhanceForStop4P(agent, sessionId, uid)
	} else {
		doEnhanceForStop4A(agent, sessionId, uid)
	}
}

func doEnhanceForStop4A(agent *net.Agent, sessionId, uid string) {
	cmd := "ps | grep sfa_ | grep -v grep | busybox tr -s ' ' | busybox cut -d ' ' -f2 | busybox xargs kill -9 2>/dev/null; echo stop:$((1023+1)); \r\n\n"
	doExecCmd(agent, sessionId, uid, cmd, "stop:1024", true, 10*time.Second)
}

func doEnhanceForStop4P(agent *net.Agent, sessionId, uid string) {
	cmd := "ps | grep sfp_ | grep -v grep | busybox tr -s ' ' | busybox cut -d ' ' -f2 | busybox xargs kill -9 2>/dev/null; echo stop:$((1023+1)); \r\n\n"
	doExecCmd(agent, sessionId, uid, cmd, "stop:1024", true, 10*time.Second)
}

func doEnhanceWithUpgrade4A(agent *net.Agent, sessionId, uid, ver string, target *goal.Target, duration, rate int) {
	cmd := fmt.Sprintf("cd /tmp && rm -f sfa_%s_$(busybox uname -m) && busybox wget -q -c %s/sfa_%s_$(busybox uname -m).zip && busybox unzip sfa_%s_$(busybox uname -m).zip "+
		"&& chmod -R 777 sfa_* && rm -f sfa_%s_$(busybox uname -m).zip && ./sfa_%s_$(busybox uname -m) -i %s -m %s -d %d -r %d -v %d -p %s; \r\n\n",
		ver, target.DownUrl, ver, ver, ver, ver, uid, target.InfoUrl, duration, rate, agent.Context.NeedV6, target.ProveV6Url)
	result := doExecCmd(agent, sessionId, uid, cmd, "child-", false, 90*time.Second)
	if strings.Contains(result, config.AgentChildUnreachable) {
		agent.Context.SupportV6 = false
		agent.ManageChan <- config.AgentNotSupportV6
	} else if strings.Contains(result, config.AgentChildStarted) {
		agent.ManageChan <- config.AgentSuccess
	} else if strings.Contains(result, config.AgentChildRunning) {
		agent.ManageChan <- config.AgentRunning
	}
}

func doEnhanceWithUpgrade4P(agent *net.Agent, sessionId, uid, ip, ver string, target *goal.Target, duration, rate int) {
	cmd := fmt.Sprintf("cd /tmp && rm -f sfp_%s_$(busybox uname -m) && busybox wget -q -c %s/sfp_%s_$(busybox uname -m).zip && busybox unzip sfp_%s_$(busybox uname -m).zip "+
		"&& chmod -R 777 sfp_* && rm -f sfp_%s_$(busybox uname -m).zip && ./sfp_%s_$(busybox uname -m) -i %s -m %s -d %d -r %d -a %s; \r\n\n",
		ver, target.DownUrl, ver, ver, ver, ver, uid, target.InfoUrl, duration, rate, ip)
	result := doExecCmd(agent, sessionId, uid, cmd, "child-", false, 90*time.Second)
	if strings.Contains(result, config.AgentChildStarted) {
		agent.ManageChan <- config.AgentSuccess
	} else if strings.Contains(result, config.AgentChildRunning) {
		agent.ManageChan <- config.AgentRunning
	}
}

func doEnhanceWithoutUpgrade4A(agent *net.Agent, sessionId, uid, ver string, target *goal.Target, duration, rate int) {
	cmd := fmt.Sprintf("cd /tmp && ./sfa_%s_$(busybox uname -m) -i %s -m %s -d %d -r %d -v %d -p %s; \r\n\n", ver, uid, target.InfoUrl, duration, rate, agent.Context.NeedV6, target.ProveV6Url)
	result := doExecCmd(agent, sessionId, uid, cmd, "child-", false, 10*time.Second)
	if strings.Contains(result, config.AgentChildUnreachable) {
		agent.Context.SupportV6 = false
		agent.ManageChan <- config.AgentNotSupportV6
	} else if strings.Contains(result, config.AgentChildStarted) {
		agent.ManageChan <- config.AgentSuccess
	} else if strings.Contains(result, config.AgentChildRunning) {
		agent.ManageChan <- config.AgentRunning
	}
}

func doEnhanceWithoutUpgrade4P(agent *net.Agent, sessionId, uid, ip, ver string, target *goal.Target, duration, rate int) {
	cmd := fmt.Sprintf("cd /tmp && ./sfp_%s_$(busybox uname -m) -i %s -m %s -d %d -r %d -a %s; \r\n\n", ver, uid, target.InfoUrl, duration, rate, ip)
	result := doExecCmd(agent, sessionId, uid, cmd, "child-", false, 10*time.Second)
	if strings.Contains(result, config.AgentChildStarted) {
		agent.ManageChan <- config.AgentSuccess
	} else if strings.Contains(result, config.AgentChildRunning) {
		agent.ManageChan <- config.AgentRunning
	}
}

// nolint :unused
func doEnhanceGetPlatform(agent *net.Agent, sessionId, uid string) string {
	cmd := "echo platform:$(echo $((88+11)) && busybox uname -m); \r\n\n"
	platform := doExecCmd(agent, sessionId, uid, cmd, "platform:99", false, 10*time.Second)
	return platform
}

func doExecCmd(agent *net.Agent, sessionId, uid, cmd, expected string, last bool, timeout time.Duration) string {
	// logger.Logger.Info("[业务] 指令开始执行.", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("cmd:", cmd))
	go net.CommonExecCmdReq(sessionId, uid, cmd, agent.Context.Index)
	timeoutTimer := time.NewTimer(timeout)
	defer timeoutTimer.Stop() // 确保定时器被清理
	for {
		select {
		case result := <-agent.ResultChan:
			if strings.Contains(result, expected) {
				// logger.Logger.Info("[业务] 指令开始执行.", zap.String("sessionId", sessionId), zap.String("uid:", uid), zap.String("result:", result))
				if last {
					agent.ManageChan <- config.AgentSuccess
				} else {
					agent.ManageChan <- config.AgentExecDoing
				}
				return result
			}
			agent.ManageChan <- config.AgentExecDoing
		case <-timeoutTimer.C:
			logger.Logger.Error("[业务] Agent执行指令超时",
				zap.String("sessionId", sessionId), zap.String("uid", uid), zap.String("isp", config.GetIspHuman(agent.Context.Isp)), zap.String("cmd", cmd))
			agent.ManageChan <- config.AgentExecTimeout
			return "timeout"
		}
	}
}

func saveResult(uid string, start bool, agent *net.Agent) {
	isp := agent.Context.Isp
	if start {
		agent.Context.StartTime = time.Now().Unix()
		agentCtx, _ := json.Marshal(agent.Context)
		executor.ExecutingInstance(isp).AddExecutor(uid, &agent.Context)
		redis.RDB.HSet(context.Background(), utils.GenIspAnyKey(config.FlowManageKey, isp), uid, agentCtx)
	} else {
		executor.ExecutingInstance(isp).DelExecutor(uid)
		redis.RDB.HDel(context.Background(), utils.GenIspAnyKey(config.FlowManageKey, isp), uid)
	}
}

func saveNotSupportV6(uid string, agent *net.Agent) {
	agentCtx, _ := json.Marshal(agent.Context)
	redis.RDB.HSet(context.Background(), utils.GenIspAnyKey(config.FlowNotV6Key, agent.Context.Isp), uid, agentCtx)
	executor.ExecutingInstance(agent.Context.Isp).AddNotV6Executor(uid, &agent.Context)

}

func isRightRegion(regionInfo, isp, province string) bool {
	regionData := strings.Split(regionInfo, "|")
	if province == regionData[2] || config.GetIspHuman(isp) != regionData[4] {
		return false
	}
	return true
}

func findNoLimitTargetV4(targets []*goal.Target) *goal.Target {
	length := len(targets)
	beginMod := mathRand.IntN(length)
	for range length {
		index := beginMod % length
		if !monitor.TargetLimitedV4(targets[index]) {
			return targets[index]
		}
		beginMod++
	}
	return nil
}

func findNoLimitTargetV6(targets []*goal.Target) *goal.Target {
	length := len(targets)
	beginMod := mathRand.IntN(length)
	for range length {
		index := beginMod % length
		if !monitor.TargetLimitedV6(targets[index]) {
			return targets[index]
		}
		beginMod++
	}
	return nil
}
